package cn.tedu.mall.front.service.impl;

import cn.tedu.mall.common.exception.CoolSharkServiceException;
import cn.tedu.mall.common.restful.JsonPage;
import cn.tedu.mall.common.restful.ResponseCode;
import cn.tedu.mall.front.mappers.FrontSpuMapper;
import cn.tedu.mall.front.service.IFrontSpuService;
import cn.tedu.mall.pojo.product.vo.SpuListItemVO;
import cn.tedu.mall.pojo.product.vo.SpuStandardVO;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.util.List;
import java.util.Random;
import java.util.concurrent.TimeUnit;

@Service
@Slf4j
public class FrontSpuServiceImpl extends FrontCacheServiceImpl<SpuStandardVO> implements IFrontSpuService  {

    private static final String FRONT_SPU_KEY = "front:spu:";
    private static final String FRONT_SPU_LOCK_SUFFIX = ".lock";

    private final FrontSpuMapper frontSpuMapper;

    @Autowired
    public FrontSpuServiceImpl(FrontSpuMapper frontSpuMapper) {
        this.frontSpuMapper = frontSpuMapper;
    }

    @Override
    public JsonPage<SpuListItemVO> listSpuByCategoryId(Long id, Integer page, Integer size) {
        /*String spuPageKey = FRONT_SPU_KEY + "categoryId:" + id + ":" + page + ":" + size;*/
        // 开启分页查询
        PageHelper.startPage(page, size);
        // 开启后 mybatis被pageHelper拦截，将会在以下执行的第一个sql查询select语句中添加limit(page-1),size
        // 结束查询后，会查询所有数量 count(*) 封装total总数 还会利用total和size计算totalPage
        List<SpuListItemVO> spuListItemVOList = frontSpuMapper.selectSpuByCategoryId(id);
        // 提供一个PageInfo对象 可以获取拦截拼接查询的分页数据
        PageInfo<SpuListItemVO> pageInfo = new PageInfo<>(spuListItemVOList);
        if (CollectionUtils.isEmpty(spuListItemVOList)) {
            throw new CoolSharkServiceException(ResponseCode.NOT_FOUND, "当前分类下Spu数据为空！");
        }
        return JsonPage.restPage(pageInfo);
    }

    @Override
    public SpuStandardVO getFrontSpuById(Long id) {
        //TODO 布隆过滤器判断存在（若存在，走下面逻辑；不存在，返回null）
        String spuKey = FRONT_SPU_KEY + "spuId:" + id;
        SpuStandardVO fromRedis = getFromRedis(spuKey);
        if (fromRedis == null) {
            // 防止击穿，先tryLock
            String spuLockKey = spuKey + FRONT_SPU_LOCK_SUFFIX;
            String spuLockRand = new Random().nextInt(9000) + 1000 + "";
            // 获取锁
            try {
                //抢到了 读数据库 ; 没抢到 等5秒 读缓存返回缓存逻辑
                if (!tryLock(spuLockKey, spuLockRand)) {
                    log.info("获取锁失败！");
                    Thread.sleep(5000);
                    return getFromRedis(spuKey);
                }
                log.info("获取锁成功！执行查询数据库逻辑");
                SpuStandardVO spuStandardVO = frontSpuMapper.selectSpuBySpuId(id);
                if (spuStandardVO == null) {
                    throw new CoolSharkServiceException(ResponseCode.NOT_FOUND, "没有此id对应的Spu数据！");
                }
                // 考虑缓存雪崩问题（大量数据同时超时），设置一个随机超时时间
                // 默认24小时超时，随机延长1~5小时
                setToRedis(spuKey, spuStandardVO, 24L * 60 * 60 + (new Random().nextInt(5 * 60 * 60) + 1), TimeUnit.SECONDS);
                return spuStandardVO;
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                releaseLock(spuLockKey, spuLockRand);
            }
        }
        return fromRedis;
    }
}
